/**!
 * koa-multipart - index.js
 * Copyright(c) 2018 Tenny
 * MIT Licensed
 */
const multiparty = require('multiparty');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const md5Hash = crypto.createHash('md5');

function md5(str) {
  return md5Hash.update(str).digest('hex');
}

let MultipartyPromise = function(req, opts) {
  return new Promise((resolve, reject) => {
    let files = [];
    let fields = {};
    let form = new multiparty.Form();
    form.on('part', (part) => {
      let body = '';
      if (!part.filename) { // 普通字段
        part.setEncoding('utf8');
        part.on('data', (chunk) => {
          body += chunk;
        });
        part.on('end', () => {
          fields[part.name] = body;
        });
        part.on('error', (err) => {
          console.error(err);
        });
      } else { // 文件字段
        let ts = typeof opts.uploadDir;
        let uploadDir = '';
        let pre = part.filename.substring(0, part.filename.lastIndexOf('.'));
        let ext = part.filename.substr(part.filename.lastIndexOf('.'));
        if (ts === 'function') {
          uploadDir = opts.uploadDir({
            name: part.name,
            filename: part.filename,
            filenamePre: pre,
            suffix: ext
          });
        } else {
          uploadDir = path.join('/', md5(pre + Date.now()) + ext);
        }
        files.push({
          fieldName: part.name,
          originalName: part.filename,
          path: uploadDir
        });
        part.pipe(fs.createWriteStream(uploadDir));
      }
    });
    form.on('error', (err) => {
      reject(err);
    });
    form.on('close', () => {
      resolve({ fields, files });
    });
    form.parse(req);
  });
};

module.exports = (opts) => {

  return async (ctx, next) => {
    // 忽略解析的情况
    if (ctx.method.toUpperCase() === 'GET' ||
      ctx.request.body ||
      !ctx.is('multipart/form-data')) {
      await next();
      return;
    }
    ctx.request.body = await MultipartyPromise(ctx.req, opts || {});
    await next();
  };
};
